#include "SmoothWindowSideAnimation.h"

///Copyright (c) 2009-2010 T. Klepzig 

map<int, SmoothWindowSideAnimation*> SmoothWindowSideAnimation::instances;
int SmoothWindowSideAnimation::instanceCounter = 138;

SmoothWindowSideAnimation::SmoothWindowSideAnimation(HWND hWnd, LPVOID tag)
{
	this->hWnd = hWnd;
	this->tag = tag;
	timerEnabled = false;
	timerId = instanceCounter++;
	
	instances[timerId] = this;

	onAnimationStartedPtr = 0;
	onAnimationCancelledPtr = 0;
	onAnimationFinishedPtr = 0;
	onValueChangedPtr = 0;
}

SmoothWindowSideAnimation::~SmoothWindowSideAnimation()
{
	StopTimer();
	instances.erase(timerId);
}


HWND SmoothWindowSideAnimation::GetHWnd()
{
	return hWnd;
}

bool SmoothWindowSideAnimation::IsRunning()
{
	return timerEnabled;
}

bool SmoothWindowSideAnimation::StartAnimation(int speed, int windowEdgeStartValue, int amount, WindowEdge windowEdge)
{
	if (!hWnd || speed < 1 || amount == 0 || timerEnabled)
		return false;

	RECT windowRect={0};
	GetWindowRect(hWnd, &windowRect);

	this->speed = speed;
	this->windowEdgeStartValue = windowEdgeStartValue;
	this->windowEdgeCurrentValue = windowEdgeStartValue;

	increment = amount > 0;
	this->amount = abs(amount);

	this->windowEdge = windowEdge;		

	if (increment)
		counter = 0;
	else
		counter = this->amount;

	StartTimer();
	CallWndSideAnimationEvent(onAnimationStartedPtr);

	return true;
}

bool SmoothWindowSideAnimation::CancelAnimation()
{
	if (timerEnabled)
	{
		StopTimer();
		CallWndSideAnimationEvent(onAnimationCancelledPtr);
		return true;
	}

	return false;
}


void SmoothWindowSideAnimation::AnimationStarted(WNDSIDEANIMATIONPROC ptr)
{
	this->onAnimationStartedPtr=ptr;
}

void SmoothWindowSideAnimation::AnimationCancelled(WNDSIDEANIMATIONPROC ptr)
{
	this->onAnimationCancelledPtr=ptr;
}

void SmoothWindowSideAnimation::AnimationFinished(WNDSIDEANIMATIONPROC ptr)
{
	this->onAnimationFinishedPtr=ptr;
}

void SmoothWindowSideAnimation::ValueChanged(WNDSIDEANIMATIONVALUECHANGEDPROC ptr)
{
	this->onValueChangedPtr = ptr;
}


void SmoothWindowSideAnimation::StartTimer()
{
	if (!timerEnabled)
	{
		SetTimer(hWnd, timerId, 10, TimerProc);
		timerEnabled=true;
	}
}

void SmoothWindowSideAnimation::StopTimer()
{
	if (timerEnabled)
	{
		KillTimer(hWnd, timerId);
		timerEnabled=false;
	}
}

int SmoothWindowSideAnimation::Round(double x)
{
	x += 0.5;
	x = floor(x);
	return (int)x;
}

void SmoothWindowSideAnimation::Animate()
{
	RECT windowRect={0};
	GetWindowRect(hWnd, &windowRect);

	if (increment)
	{
		if (windowEdgeCurrentValue <= ((windowEdgeStartValue + amount) - speed))
		{
			counter += speed;
			windowEdgeCurrentValue += speed;

			if (windowEdge == WE_WIDTH)
				SetWindowPos(hWnd, 0, 0, 0, RECTWIDTH(windowRect) + speed, RECTHEIGHT(windowRect), SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
			else if (windowEdge == WE_HEIGHT)
				SetWindowPos(hWnd, 0, 0, 0, RECTWIDTH(windowRect), RECTHEIGHT(windowRect) + speed, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);

			CallWndSideValueChangedEvent(onValueChangedPtr, speed, counter);

			if (counter > (amount - amount / (double)5) && speed > 2)
			speed /= 2;
		}
		else
		{
			if (windowEdge == WE_WIDTH)
				SetWindowPos(hWnd, 0, 0, 0, windowEdgeStartValue + amount, RECTHEIGHT(windowRect), SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
			else if (windowEdge == WE_HEIGHT)
				SetWindowPos(hWnd, 0, 0, 0, RECTWIDTH(windowRect), windowEdgeStartValue + amount, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);

			StopTimer();
			CallWndSideValueChangedEvent(onValueChangedPtr, speed, counter);
			CallWndSideAnimationEvent(onAnimationFinishedPtr);
		}	
	}
	else
	{

		if (windowEdgeCurrentValue >= ((windowEdgeStartValue - amount) + speed))
		{
			counter -= speed;
			windowEdgeCurrentValue -= speed;

			if (windowEdge == WE_WIDTH)
				SetWindowPos(hWnd, 0, 0, 0, RECTWIDTH(windowRect) - speed, RECTHEIGHT(windowRect), SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
			else if (windowEdge == WE_HEIGHT)
				SetWindowPos(hWnd, 0, 0, 0, RECTWIDTH(windowRect), RECTHEIGHT(windowRect) - speed, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
			
			CallWndSideValueChangedEvent(onValueChangedPtr, -speed, counter);

			if (counter < (amount / (double)5) && speed > 2)
			speed /= 2;
		}
		else
		{
			if (windowEdge == WE_WIDTH)
				SetWindowPos(hWnd, 0, 0, 0, windowEdgeStartValue - amount, RECTHEIGHT(windowRect), SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
			else if (windowEdge == WE_HEIGHT)
				SetWindowPos(hWnd, 0, 0, 0, RECTWIDTH(windowRect), windowEdgeStartValue - amount, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);

			StopTimer();
			CallWndSideValueChangedEvent(onValueChangedPtr, -speed, counter);
			CallWndSideAnimationEvent(onAnimationFinishedPtr);
		}	
	}
}

void SmoothWindowSideAnimation::CallWndSideAnimationEvent(WNDSIDEANIMATIONPROC ptr)
{
	if (ptr)
		ptr(this, tag);
}

void SmoothWindowSideAnimation::CallWndSideValueChangedEvent(WNDSIDEANIMATIONVALUECHANGEDPROC ptr, int step, int currentValue)
{
	if (ptr)
		ptr(this, step, currentValue, tag);
}


VOID CALLBACK SmoothWindowSideAnimation::TimerProc(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
	if (instances.find(idEvent) != instances.end())
		instances[idEvent]->Animate();
}
